package com.seleniti.enpareja.controller.ui.view;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.Set;

import org.ioc.commons.flowcontrol.actioncontroller.ActionController;
import org.ioc.commons.flowcontrol.actioncontroller.ActionHandler;
import org.ioc.commons.flowcontrol.common.impl.ReceiverAdapter;
import org.ioc.commons.flowcontrol.eventbus.EventBus;
import org.ioc.commons.flowcontrol.logincontroller.IsLoggedUserInfoConsumer;
import org.ioc.commons.flowcontrol.messageboxfactory.MessageBoxFactory.ConfirmResponse;
import org.ioc.commons.flowcontrol.operationmanager.OperationManager;
import org.ioc.commons.flowcontrol.operationmanager.impl.OperationHandlerFactory;
import org.ioc.commons.model.globaldata.GlobalDataController.Consumer;
import org.ioc.commons.model.globaldata.IsGlobalDataConsumer;
import org.ioc.commons.utils.Format;
import org.ioc.commons.utils.FormatterLogger;
import org.ioc.commons.utils.ObjectsCompat;

import com.seleniti.enpareja.controller.common.GlobalData;
import com.seleniti.enpareja.controller.common.domain.LoggedUserInfo;
import com.seleniti.enpareja.controller.model.integration.database.domain.Couple;
import com.seleniti.enpareja.controller.model.integration.database.domain.CoupleType;
import com.seleniti.enpareja.controller.model.integration.database.domain.SharedExpense;
import com.seleniti.enpareja.controller.model.integration.database.domain.SharedExpense.MadeBy;
import com.seleniti.enpareja.controller.ui.base.EnParejaViewBasePresenter;
import com.seleniti.enpareja.controller.ui.main.AppShell;
import com.seleniti.enpareja.controller.ui.view.SharedExpensesView.Action;
import com.seleniti.enpareja.controller.ui.view.SharedExpensesView.ActionMode;
import com.seleniti.enpareja.controller.ui.view.SharedExpensesView.Board;
import com.seleniti.enpareja.controller.ui.view.SharedExpensesView.DebtsStatus;
import com.seleniti.enpareja.controller.ui.view.SharedExpensesView.Event;
import com.seleniti.enpareja.controller.ui.view.SharedExpensesView.Operation;

/**
 * Presenter for the Login view
 * 
 * @author Jesús Lunar Pérez
 * 
 */
public class SharedExpensesViewPresenter
		extends
		EnParejaViewBasePresenter<SharedExpensesView, SharedExpensesView.Event, SharedExpensesView.Operation, SharedExpensesView.Action>
		implements SharedExpensesView.Presenter, IsLoggedUserInfoConsumer<LoggedUserInfo>,
		IsGlobalDataConsumer<GlobalData> {

	private static final FormatterLogger logger = FormatterLogger.getLogger(SharedExpensesViewPresenter.class);
	private SharedExpense lastModified;
	private Consumer<GlobalData> consumer;

	public SharedExpensesViewPresenter(SharedExpensesView display, EventBus<Event> eventbus,
			OperationManager<Operation> operationManager, ActionController<Action> actionController) {
		super(display, eventbus, operationManager, actionController);
	}

	@Override
	public void onInitDisplay() {
		super.onInitDisplay();
		this.consumer = this.globalDataController.registerGlobalDataConsumer(this);

		display.setActionMode(ActionMode.MODIFY_ELEMENT);
		this.consumer.requires(GlobalData.UserAgent);
		this.loginController.requiresLoggedUserInfoIfDisplayActive(this);
	}

	@Override
	protected void bindComponents() {
		super.bindComponents();
		operationManager.bindOperationsRunning(OperationHandlerFactory.<Operation> forLoaders(display));
		operationManager.bindOperationsRunning(OperationHandlerFactory.<Operation> forLoaders(shell.globalLoader()));

		shell.actionController().bindAction(AppShell.Action.DeleteNow, new ActionHandler<AppShell.Action>() {

			@Override
			public void handleAction(Object sender, com.seleniti.enpareja.controller.ui.main.AppShell.Action action,
					Object... params) {
				deleteSelected();
			}
		});
	}

	protected void deleteSelected() {
		if (display.getActionMode() == ActionMode.DELETE_ELEMENT) {
			Set<SharedExpense> selection = display.getSelection();
			Couple couple = display.couple().getValue();

			if (couple.getSharedExpenses() != null) {
				couple.getSharedExpenses().removeAll(selection);
				saveCouple(Operation.Saving, operationManager, true);
				display.setActionMode(ActionMode.MODIFY_ELEMENT);
			}
		}

	}

	@Override
	public void onActive() {
		super.onActive();

		refreshData();

		shell.refreshButtonVisibility().setVisible(true);

		LoggedUserInfo lui = this.loginController.getLoggedUserInfo();

		display.setEnabled(lui != null && lui.getCoupleName() != null && !lui.getCoupleName().isEmpty());

		shell.nonAssociatedPanelVisibility()
				.setVisible(
						lui != null
								&& lui.isLogged()
								&& (lui.getCoupleName() == null || lui.getCoupleName().isEmpty() || lui.getCoupleType() == CoupleType.TESTING_MODE_COUPLE));

		shell.firstStepsVisibility().setVisible(
				shell.nonAssociatedPanelVisibility().isVisible()
						&& lui.getCoupleType() != CoupleType.TESTING_MODE_COUPLE);
		shell.nonAssociatedButtonVisibility().setVisible(!shell.firstStepsVisibility().isVisible());

		this.consumer.ensureRequirements();
	}

	@Override
	public void onRequiredGlobalDataReady() {
		showGoBackButtonIfRequired();
	}

	@Override
	public void onRequiredGlobalDataNotReadyYet() {
		/*
		 * Nothing to do
		 */
	}

	@Override
	public void onRequiredGlobalDataUpdated(GlobalData globalDataUpdated) {
		showGoBackButtonIfRequired();
	}

	@Override
	protected void refreshData() {
		super.refreshCouple(new ReceiverAdapter<Couple>() {

			@Override
			public void success(Couple result) {
				display.elementList().clear();
				if (result != null && result.getSharedExpenses() != null) {
					display.elementList().addAll(result.getSharedExpenses());
				}

				refreshTotals(result.getSharedExpenses(), result != null
						&& result.getCoupleType() != CoupleType.USERS_COUPLE);

				logger.info("Couple type: {0}", result != null ? result.getCoupleType() : null);

				boolean member1ActionsVisible = result != null && result.getMember1() != null
						&& !result.getMember1().isFakeUser();
				boolean member2ActionsVisible = result != null && result.getMember2() != null
						&& !result.getMember2().isFakeUser();

				display.member1ActionsVisible().setVisible(member1ActionsVisible);
				display.member2ActionsVisible().setVisible(member2ActionsVisible);
				display.notSharedActionEnabled().setEnabled(
						result != null && result.getCoupleType() == CoupleType.USERS_COUPLE);

				display.member1Name().setValue(
						result != null && result.getMember1() != null && result.getMember1().getNickName() != null
								&& !result.getMember1().getNickName().isEmpty() ? result.getMember1().getNickName()
								: "Tú");
				display.member2Name().setValue(
						result != null && result.getMember2() != null && result.getMember2().getNickName() != null
								&& !result.getMember2().getNickName().isEmpty() ? result.getMember2().getNickName()
								: "Tu pareja");

				if (lastModified != null) {
					int idx = display.elementList().indexOf(lastModified);
					if (idx < 0 && lastModified.getSavedAt() != null) {
						idx = indexOfBySavedAt(lastModified, display.elementList());
					}

					if (idx >= 0) {
						logger.debug("Element {0} found at pos {1}/{2}", lastModified, idx, display.elementList()
								.size());
						display.scrollToElement(idx);
					} else {
						logger.info("Element {0} wasn't found in the list of elements", lastModified);
					}

				} else {
					logger.info("No last modified element");
				}
			}
		}, Operation.GettingData, operationManager, "member1", "member2", "sharedExpenses");

	}

	protected int indexOfBySavedAt(SharedExpense element, List<SharedExpense> elementList) {
		for (int idx = 0; idx < elementList.size(); idx++) {
			SharedExpense se = elementList.get(idx);
			if (ObjectsCompat.equals(element.getSavedAt(), se.getSavedAt())) {
				return idx;
			}
		}

		return -1;
	}

	protected void refreshTotals(List<SharedExpense> expenses, boolean single) {
		if (expenses.isEmpty()) {
			display.totalMember1().setValue(null);
			display.totalMember2().setValue(null);
		} else {
			BigDecimal totalMember1 = new BigDecimal(0.0);
			BigDecimal totalMember2 = new BigDecimal(0.0);
			BigDecimal totalDebt = new BigDecimal(0.0);

			logger.info("Adding up {0} expenses...", expenses.size());

			boolean total1IncludeNotShared = false;
			boolean total2IncludeNotShared = false;

			for (SharedExpense expense : expenses) {
				if (expense.getAmount() == null) {
					logger.info("Ignoring expense {0}. No amount.", expense);
					continue;
				}

				BigDecimal debt = null;

				boolean shared = expense.isShared();
				if (shared) {
					debt = expense.getAmount().divide(new BigDecimal(2));
				} else {
					debt = expense.getAmount();
				}

				switch (expense.getMadeBy()) {
				case MEMBER_1:
					totalMember1 = totalMember1.add(expense.getAmount());
					totalDebt = totalDebt.add(debt);
					total1IncludeNotShared |= !shared;
					break;
				case MEMBER_2:
					totalMember2 = totalMember2.add(expense.getAmount());
					totalDebt = totalDebt.subtract(debt);
					total2IncludeNotShared |= !shared;
					break;
				}
			}

			totalMember1.setScale(2, RoundingMode.HALF_UP);
			totalMember2.setScale(2, RoundingMode.HALF_UP);
			totalDebt.setScale(2, RoundingMode.HALF_UP);

			int comp = totalDebt.compareTo(BigDecimal.ZERO);
			if (comp == 0) {
				display.setDebtsStatus(DebtsStatus.CLEAN, totalDebt.abs());
			} else if (single) {
				display.setDebtsStatus(DebtsStatus.SINGLE, totalDebt.abs());
			} else if (comp < 0) {
				display.setDebtsStatus(DebtsStatus.MEMBER1_OWES, totalDebt.abs());
			} else {
				display.setDebtsStatus(DebtsStatus.MEMBER2_OWES, totalDebt.abs());
			}

			display.totalMember1().setValue(totalMember1);
			display.totalMember2().setValue(!single ? totalMember2 : null);
			display.total1IncludeNotShared().setValue(total1IncludeNotShared);
			display.total2IncludeNotShared().setValue(total2IncludeNotShared);
		}

	}

	@Override
	public void onLoggedUserInfoDataReady() {
		refreshData();
	}

	@Override
	public void onLoggedUserInfoDataUpdated(LoggedUserInfo loggedUserInfo) {
	}

	@Override
	public void onLoggedUserInfoNotReadyYet() {
	}

	@Override
	public void onActionModeChanged() {
		ActionMode mode = display.getActionMode();

		logger.info("Action mode changed: {0}", mode);

		switch (mode) {
		case DELETE_ELEMENT:
			this.shell.deleteButtonVisibility().setVisible(true);
			break;
		default:
			this.shell.deleteButtonVisibility().setVisible(false);
		}

	}

	@Override
	public void onSelectionChanged() {
		ActionMode mode = display.getActionMode();

		logger.info("Selection changed on mode {0}", mode);

		if (mode == ActionMode.MODIFY_ELEMENT) {
			Set<SharedExpense> selection = display.getSelection();

			if (!selection.isEmpty()) {
				final SharedExpense single = selection.iterator().next();
				display.showModifyForm(single);

				display.clearSelection();
			}
		}

	}

	@Override
	public void onAddSharedExpense(MadeBy madeBy) {
		SharedExpense element = new SharedExpense();

		element.setDate(new Date());
		element.setMadeBy(madeBy);
		element.setSavedAt(new Date());

		display.showModifyForm(element);
	}

	@Override
	public void onElementModified() {
		if (display.checkformValidity()) {
			Couple couple = display.couple().getValue();

			display.hideModificationForm();

			SharedExpense modifyingElement = display.modifyingElement();
			this.lastModified = modifyingElement;

			modifyingElement.setDate(display.formDate().getValue());
			modifyingElement.setAmount(display.formAmount().getValue());
			modifyingElement.setText(Format.capitalize(display.formText().getValue()));
			modifyingElement.setShared(display.formShared().getValue());
			modifyingElement.setSavedAt(new Date());

			if (!couple.getSharedExpenses().contains(modifyingElement)) {
				couple.getSharedExpenses().add(modifyingElement);
			}

			saveCouple(Operation.Saving, operationManager, true);

		} else {
			messageBoxFactory.alert("Gasto", "Debes rellenar lo que falta");
		}
	}

	@Override
	public void onCleanBill() {
		messageBoxFactory.confirm("Liquidar deudas",
				"¿Estas seguro de que quieres liquidar todas las deudas? (se eliminarán todas y empezaréis de cero)",
				new ConfirmResponse() {

					@Override
					public void onUserResponse(boolean userResponse) {
						if (userResponse) {
							Couple couple = display.couple().getValue();
							couple.getSharedExpenses().clear();
							saveCouple(Operation.Saving, operationManager, true);
						}
					}
				});

	}

	@Override
	public boolean onGoBoardBack() {
		if (this.display.boardShown().getValue() != Board.MAIN_LIST) {
			this.display.boardShown().setValue(Board.MAIN_LIST);
			return true;
		}

		return false;
	}
}
